import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/services.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:google_mlkit_commons/google_mlkit_commons.dart';

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();
  const MethodChannel channel = MethodChannel('google_mlkit_text_recognizer');

  // Mock MethodChannel response
  setUp(() {
    channel.setMockMethodCallHandler((MethodCall methodCall) async {
      if (methodCall.method == 'vision#startTextRecognizer') {
        // Return mock data for text recognition
        return {
          'text': 'Hello World!',
          'blocks': [
            {
              'text': 'Hello World!',
              'rect': {'left': 0, 'top': 0, 'right': 100, 'bottom': 50},
              'recognizedLanguages': ['en'],
              'points': [
                {'x': 0, 'y': 0},
                {'x': 100, 'y': 0},
                {'x': 100, 'y': 50},
                {'x': 0, 'y': 50}
              ],
              'lines': [
                {
                  'text': 'Hello World!',
                  'rect': {'left': 0, 'top': 0, 'right': 100, 'bottom': 50},
                  'recognizedLanguages': ['en'],
                  'points': [
                    {'x': 0, 'y': 0},
                    {'x': 100, 'y': 0},
                    {'x': 100, 'y': 50},
                    {'x': 0, 'y': 50}
                  ],
                  'confidence': 0.95,
                  'angle': 0.0,
                  'elements': [
                    {
                      'text': 'Hello',
                      'rect': {'left': 0, 'top': 0, 'right': 50, 'bottom': 50},
                      'recognizedLanguages': ['en'],
                      'points': [
                        {'x': 0, 'y': 0},
                        {'x': 50, 'y': 0},
                        {'x': 50, 'y': 50},
                        {'x': 0, 'y': 50}
                      ],
                      'confidence': 0.95,
                      'angle': 0.0,
                      'symbols': [
                        {
                          'text': 'H',
                          'rect': {'left': 0, 'top': 0, 'right': 10, 'bottom': 50},
                          'recognizedLanguages': ['en'],
                          'points': [
                            {'x': 0, 'y': 0},
                            {'x': 10, 'y': 0},
                            {'x': 10, 'y': 50},
                            {'x': 0, 'y': 50}
                          ],
                          'confidence': 0.95,
                          'angle': 0.0
                        }
                      ]
                    },
                    {
                      'text': 'World!',
                      'rect': {'left': 55, 'top': 0, 'right': 100, 'bottom': 50},
                      'recognizedLanguages': ['en'],
                      'points': [
                        {'x': 55, 'y': 0},
                        {'x': 100, 'y': 0},
                        {'x': 100, 'y': 50},
                        {'x': 55, 'y': 50}
                      ],
                      'confidence': 0.95,
                      'angle': 0.0,
                      'symbols': []
                    }
                  ]
                }
              ]
            }
          ]
        };
      } else if (methodCall.method == 'vision#closeTextRecognizer') {
        // Return null for close method
        return null;
      }
      return null;
    });
  });

  // Remove mock after each test
  tearDown(() {
    channel.setMockMethodCallHandler(null);
  });

  group('TextRecognizer', () {
    test('can be initialized with default script', () {
      final recognizer = TextRecognizer();
      expect(recognizer.script, TextRecognitionScript.latin);
      expect(recognizer.id, isNotNull);
      recognizer.close();
    });

    test('can be initialized with specific script', () {
      final recognizer = TextRecognizer(script: TextRecognitionScript.chinese);
      expect(recognizer.script, TextRecognitionScript.chinese);
      expect(recognizer.id, isNotNull);
      recognizer.close();
    });

    test('processImage returns RecognizedText with mock data', () async {
      final recognizer = TextRecognizer();
      
      // Create a mock InputImage
      final inputImage = InputImage.fromFilePath('path/to/image.jpg');
      
      // Process the image
      final result = await recognizer.processImage(inputImage);
      
      // Verify the result
      expect(result, isInstanceOf<RecognizedText>());
      expect(result.text, 'Hello World!');
      expect(result.blocks.length, 1);
      expect(result.blocks[0].text, 'Hello World!');
      expect(result.blocks[0].lines.length, 1);
      expect(result.blocks[0].lines[0].text, 'Hello World!');
      expect(result.blocks[0].lines[0].elements.length, 2);
      expect(result.blocks[0].lines[0].elements[0].text, 'Hello');
      expect(result.blocks[0].lines[0].elements[1].text, 'World!');
      
      recognizer.close();
    });

    test('processImage returns empty RecognizedText when result is null', () async {
      // Override mock to return null
      channel.setMockMethodCallHandler((MethodCall methodCall) async {
        if (methodCall.method == 'vision#startTextRecognizer') {
          return null;
        }
        return null;
      });

      final recognizer = TextRecognizer();
      final inputImage = InputImage.fromFilePath('path/to/image.jpg');
      final result = await recognizer.processImage(inputImage);

      expect(result, isInstanceOf<RecognizedText>());
      expect(result.text, '');
      expect(result.blocks.length, 0);

      recognizer.close();
    });

    test('close method is called', () async {
      final recognizer = TextRecognizer();
      final inputImage = InputImage.fromFilePath('path/to/image.jpg');
      
      // Process the image first
      await recognizer.processImage(inputImage);
      
      // Then close the recognizer
      expect(recognizer.close(), completes);
    });

    test('TextRecognitionScript enum values are as expected', () {
      expect(TextRecognitionScript.latin.index, 0);
      expect(TextRecognitionScript.chinese.index, 1);
      expect(TextRecognitionScript.devanagiri.index, 2);
      expect(TextRecognitionScript.japanese.index, 3);
      expect(TextRecognitionScript.korean.index, 4);
    });
  });

  group('RecognizedText', () {
    test('fromJson creates instance correctly', () {
      final json = {
        'text': 'Test Text',
        'blocks': [
          {
            'text': 'Block Text',
            'rect': {'left': 0, 'top': 0, 'right': 100, 'bottom': 50},
            'recognizedLanguages': ['en'],
            'points': [
              {'x': 0, 'y': 0},
              {'x': 100, 'y': 0},
              {'x': 100, 'y': 50},
              {'x': 0, 'y': 50}
            ],
            'lines': []
          }
        ]
      };

      final recognizedText = RecognizedText.fromJson(json);

      expect(recognizedText.text, 'Test Text');
      expect(recognizedText.blocks.length, 1);
      expect(recognizedText.blocks[0].text, 'Block Text');
    });

    test('fromJson handles empty blocks correctly', () {
      final json = {
        'text': 'Test Text',
        'blocks': null
      };

      final recognizedText = RecognizedText.fromJson(json);

      expect(recognizedText.text, 'Test Text');
      expect(recognizedText.blocks.length, 0);
    });
  });

  group('TextBlock', () {
    test('fromJson creates instance correctly', () {
      final json = {
        'text': 'Block Text',
        'rect': {'left': 0, 'top': 0, 'right': 100, 'bottom': 50},
        'recognizedLanguages': ['en'],
        'points': [
          {'x': 0, 'y': 0},
          {'x': 100, 'y': 0},
          {'x': 100, 'y': 50},
          {'x': 0, 'y': 50}
        ],
        'lines': [
          {
            'text': 'Line Text',
            'rect': {'left': 0, 'top': 0, 'right': 100, 'bottom': 25},
            'recognizedLanguages': ['en'],
            'points': [
              {'x': 0, 'y': 0},
              {'x': 100, 'y': 0},
              {'x': 100, 'y': 25},
              {'x': 0, 'y': 25}
            ],
            'confidence': 0.95,
            'angle': 0.0,
            'elements': []
          }
        ]
      };

      final textBlock = TextBlock.fromJson(json);

      expect(textBlock.text, 'Block Text');
      expect(textBlock.boundingBox.left, 0);
      expect(textBlock.boundingBox.top, 0);
      expect(textBlock.boundingBox.right, 100);
      expect(textBlock.boundingBox.bottom, 50);
      expect(textBlock.recognizedLanguages, ['en']);
      expect(textBlock.cornerPoints.length, 4);
      expect(textBlock.lines.length, 1);
      expect(textBlock.lines[0].text, 'Line Text');
    });

    test('fromJson handles null lines correctly', () {
      final json = {
        'text': 'Block Text',
        'rect': {'left': 0, 'top': 0, 'right': 100, 'bottom': 50},
        'recognizedLanguages': ['en'],
        'points': [
          {'x': 0, 'y': 0},
          {'x': 100, 'y': 0},
          {'x': 100, 'y': 50},
          {'x': 0, 'y': 50}
        ],
        'lines': null
      };

      final textBlock = TextBlock.fromJson(json);

      expect(textBlock.lines.length, 0);
    });
  });
}